using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.Drawing;

namespace Assignment_2
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        #region Variables
        private GraphicsDeviceManager graphics;
        private BasicEffect effect;
        private FPS fps;
        private Camera camera;
        private RasterizerState WIREFRAME_RASTERIZER_STATE = new RasterizerState() { CullMode = CullMode.None, FillMode = FillMode.Solid };
        private Model model;
        private Texture2D worldTexture;
        private Texture2D heightMap;
        private TheWorldSphere theWorld;
        private TheWaterSphere theWater;
        private Texture2D waterTexture;
        private Texture2D cloudTexture;
        private TheCloudSphere theCloud;
        private Robot robot;
        private Matrix world;
        private List<GameObject> objects = new List<GameObject>();
        private float waterSize;
        private float minWaterSize;
        #endregion

        #region Constants
        private const float MAXWATERSIZE = 1f;

        // Alpha value can only be between 0 (fully transparent) and 1
        private const float WATERALPHAVALUE = .4f;
        private const float CLOUDALPHAVALUE = .3f;
        #endregion

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferHeight = 480;
            graphics.PreferredBackBufferWidth = 800;
            graphics.IsFullScreen = false;
            graphics.SynchronizeWithVerticalRetrace = false;

            Content.RootDirectory = "Content";

            // Disabled
            #region FPS
#if DEBUG
            //fps = new FPS(this);
#else
            fps = new FPS(this,true,true,this.TargetElapsedTime);
#endif
            //Components.Add(fps);
            #endregion
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            #region Load Content
            int slices = 100;
            int stacks = 100;
            SphereStuff wf = new SphereStuff();

            world = Matrix.Identity;

            GraphicsDevice.BlendState = BlendState.AlphaBlend;

            heightMap = Content.Load<Texture2D>("globalearthtopo");
            worldTexture = Content.Load<Texture2D>("WorldTexture");
            model = Content.Load<Model>("Grund");
            waterTexture = Content.Load<Texture2D>("WaterTexture");
            cloudTexture = Content.Load<Texture2D>("CloudTexture");

            wf.TextureTo2DArray(heightMap);            

            theWorld = new TheWorldSphere(GraphicsDevice, wf, slices, stacks, 1, world, heightMap);
            theWater = new TheWaterSphere(GraphicsDevice, slices, stacks, 1, world);
            theCloud = new TheCloudSphere(GraphicsDevice, slices, stacks, 1, world);

            minWaterSize = theWorld.minWaterSize;
            waterSize = minWaterSize;
            theWater.scale = waterSize;
            robot = new Robot(GraphicsDevice, wf, model, new Vector3(0, 1.03f, 0), slices, stacks, world);
            this.camera = new Camera(this, robot);

            GraphicsDevice.RasterizerState = WIREFRAME_RASTERIZER_STATE;
            effect = new BasicEffect(graphics.GraphicsDevice);
            effect.TextureEnabled = true;
            effect.Projection = this.camera.Projection;
            effect.View = this.camera.View;
            effect.World = world;
            effect.LightingEnabled = true;
            #endregion
        }
        

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            #region Update
            GetUserInput(gameTime);

            theWorld.Update(gameTime, world);
            theWater.Update(gameTime, world);
            theCloud.Update(gameTime, world);
            robot.Update(gameTime, world);            
            camera.Update(gameTime);

            effect.View = this.camera.View;
            effect.Projection = this.camera.Projection;

            base.Update(gameTime);
            #endregion
        }

        /// <summary>
        /// Does something depending on the input the user is giving.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        private void GetUserInput(GameTime gameTime)
        {
            #region Key control
            // Allows the game to exit
            if (Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();
            
            if (Keyboard.GetState().IsKeyDown(Keys.Right) || Keyboard.GetState().IsKeyDown(Keys.D))
            {
                robot.Move(Constants.Direction.Right, gameTime);
                //float rotationSpeed = (float)gameTime.ElapsedGameTime.TotalSeconds * 1.0f;
                //theWorld.Rotate(rotationSpeed, Constants.Direction.Right);
                //theWater.Rotate(rotationSpeed, Constants.Direction.Right);
                //theCloud.Rotate(rotationSpeed, Constants.Direction.Right);
            }

            if (Keyboard.GetState().IsKeyDown(Keys.Left) || Keyboard.GetState().IsKeyDown(Keys.A))
            {
                robot.Move(Constants.Direction.Left, gameTime);
                //float rotationSpeed = (float)gameTime.ElapsedGameTime.TotalSeconds * 1.0f;
                //theWorld.Rotate(rotationSpeed, Constants.Direction.Left);
                //theWater.Rotate(rotationSpeed, Constants.Direction.Left);
                //theCloud.Rotate(rotationSpeed, Constants.Direction.Left);
            }

            if (Keyboard.GetState().IsKeyDown(Keys.Up) || Keyboard.GetState().IsKeyDown(Keys.W))
            {
                robot.Move(Constants.Direction.Forward, gameTime);
                //float rotationSpeed = (float)gameTime.ElapsedGameTime.TotalSeconds * 1.0f;
                //theWorld.Rotate(rotationSpeed, Constants.Direction.Forward);
                //theWater.Rotate(rotationSpeed, Constants.Direction.Forward);
                //theCloud.Rotate(rotationSpeed, Constants.Direction.Forward);
            }
            else if (Keyboard.GetState().IsKeyDown(Keys.Down) || Keyboard.GetState().IsKeyDown(Keys.S))
            {
                robot.Move(Constants.Direction.Backward, gameTime);
                //float rotationSpeed = (float)gameTime.ElapsedGameTime.TotalSeconds * 0.5f;
                //theWorld.Rotate(rotationSpeed, Constants.Direction.Backward);
                //theWater.Rotate(rotationSpeed, Constants.Direction.Backward);
                //theCloud.Rotate(rotationSpeed, Constants.Direction.Backward);
            }

            if (Keyboard.GetState().IsKeyDown(Keys.F))
            {
                waterSize += (float)gameTime.ElapsedGameTime.TotalSeconds * 0.05f;
                if (waterSize > MAXWATERSIZE)
                    waterSize = MAXWATERSIZE;
                theWater.scale = waterSize;
            }

            if (Keyboard.GetState().IsKeyDown(Keys.V))
            {
                waterSize -= (float)gameTime.ElapsedGameTime.TotalSeconds * 0.05f;
                if (waterSize < minWaterSize)
                    waterSize = minWaterSize;
                theWater.scale = waterSize;
            }
            #endregion
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            #region Draw
            GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.Black);

            // Texure needs to be set to "clamp" when the image size isnt the power of 2.
            SamplerState ss = new SamplerState();
            ss.AddressU = TextureAddressMode.Clamp;
            ss.AddressV = TextureAddressMode.Clamp;
            GraphicsDevice.SamplerStates[0] = ss;

            effect.Texture = worldTexture;
            effect.Alpha = 1f;
            theWorld.Draw(effect, world);

            robot.Draw(effect, world);

            effect.Texture = waterTexture;
            effect.Alpha = WATERALPHAVALUE;
            theWater.Draw(effect, world);

            effect.Texture = cloudTexture;
            effect.Alpha = CLOUDALPHAVALUE;
            theCloud.Draw(effect, world);

            base.Draw(gameTime);
            #endregion
        }
    }
}
